<?php
/** 组元类和组合类都要实现的接口 */
interface FileNode
{
    /** 通用方法 */
    /** @return bool 是否是目录 */
    public function isDir();
    /** 
     * 设置上级目录
     * @param FileNode $node
     * @return bool 
     */
    public function setDirectory($node);
    /** 
     * 移动到一个节点下
     * @param FileNode $node
     * @return bool 
     */
    public function moveTo($node);
    /** @return bool 删除节点 */
    public function remove();

    /** 文件方法 */
    /** @return bool 写入内容 */
    public function write($content);
    /** @return string 显示内容 */
    public function read();

    /** 目录方法 */
    /** 
     * 添加子节点
     * @param FileNode $node
     * @return bool 
     */
    public function addChild($node);
    /** 
     * 移除子节点
     * @param FileNode $node
     * @return bool 
     */
    public function removeChild($node);
    /** @return FileNode[] 返回所有子节点 */
    public function getChildren();
}
/** Component 组元 */
class File implements FileNode
{
    /** @var FileNode $directory */
    protected $directory=null;
    /** @var string $content */
    protected $content="";
    public function __construct($content="")
    {
        $this->content = $content;
        echo "文件创建完成\r\n";
    }
    public function isDir()
    {
        return false;
    }
    public function setDirectory($node)
    {
        $this->directory=$node;
        return true;
    }
    public function moveTo($node)
    {
        if($node === $this->directory) return true;
        $node->addChild($this);
        if($result=$this->directory->removeChild($this)){
            echo "文件移动完成\r\n";
        }
        return $result;
    }
    public function remove()
    {
        return $this->directory->removeChild($this);
    }

    public function write($content)
    {
        $this->content=$content;
        echo "文件写入完成\r\n";
        return true;
    }
    public function read()
    {
        return $this->content;
    }

    public function addChild($node)
    {
        throw new Exception("文件无法添加子节点");
    }
    public function removeChild($node)
    {
        throw new Exception("文件无法移除子节点");
    }
    public function getChildren()
    {
        throw new Exception("文件无法获取子节点");
    }

    public function __destruct()
    {
        echo "文件销毁完成\r\n";
    }
}
/** Composite 组合 */
class FileDirectory implements FileNode
{
    /** @var FileNode $directory */
    protected $directory=null;
    /** @var FileNode[] $children */
    protected $children=[];
    public function __construct()
    {
        echo "目录创建完成\r\n";
    }
    public function isDir()
    {
        return true;
    }
    public function setDirectory($node)
    {
        $this->directory=$node;
        return true;
    }
    public function moveTo($node)
    {
        if($node === $this->directory) return true;
        $node->addChild($this);
        if($result=$this->directory->removeChild($this)){
            echo "目录移动完成\r\n";
        }
        return $result;
    }
    public function remove()
    {
        return $this->directory->removeChild($this);
    }
    public function write($content)
    {
        throw new Exception("目录无法写入内容");
    }
    public function read()
    {
        throw new Exception("目录无法读取内容");
    }
    public function addChild($node)
    {
        if(in_array($node,$this->children)){
            throw new Exception("文件已存在");
        }
        $node->setDirectory($this);
        $this->children[]=$node;
        return true;
    }
    public function removeChild($node)
    {
        if($index=array_search($node,$this->children)){
            unset($this->children[$index]);
            return true;
        }
        throw new Exception("文件不存在");
    }
    public function getChildren()
    {
        return $this->children;
    }
    public function __destruct()
    {
        echo "目录销毁完成\r\n";
    }
}

header('Content-Type: text/plain');

$root=new FileDirectory();
$root->addChild(new File("hello world"));

$dir=new FileDirectory;
$root->addChild($dir);
$dir->addChild(new File("cdfgvrfrefgyr"));

/**
 * 遍历所有文件并输出内容
 * @param FileNode $node
 */
function traverse($node)
{
    if(!$node->isDir()) echo $node->read() ."\r\n";
    else {
        foreach ($node->getChildren() as $key => $value) {
            traverse($value);
        }
    }
}
traverse($root);

$dir->remove($dir->getChildren()[0]);
echo "脚本结束\r\n";
